LOADING...

加载过慢请开启缓存(浏览器默认开启)

loading

随机面试题

2024/3/6 前端
  • JavaScript作用域和闭包
    Q:解释 JavaScript 中的词法作用域(Lexical Scoping)和闭包(Closure)。闭包是如何工作的,它们在实际开发中有哪些用途?
    A:词法作用域是指一个函数的内部作用域,闭包是函数以及其周围的此法环境,它通常是一个函数,这个函数能访问外部的变量,因此这个变量一直存续在函数存续期间;在实际开发过程中,闭包可以用于将函数和他词法环境绑定,比如计算长方形的面积,当他的宽固定时,就可以写作:

    function calcArea(width){
        return (height)=>{
            return width*height;
        }
    }
    const area = calcArea(10);
    area(20);
    

    另外,闭包也用于节流、防抖等,但要注意错误的使用闭包会造成内存泄漏。
    C:实际应用:数据封装(模拟私有变量)、函数柯里化、事件处理器和回调函数

  • 异步编程
    Q:解释 Promiseasync/await 在 JavaScript 中的作用。如何使用它们处理异步操作?请给出一个使用 async/await 处理 HTTP 请求并捕获错误的示例代码。
    A:由于js是单线程的,所以一些网络请求等需求需要异步实现,最早使用的方式是回调函数,但是当一个回调函数依赖于另一个回调函数的时候,就会造成回调地狱,promise为了解决这一问题出现,promise的构造函数接受resolve和reject两个参数;async/await是promise的语法糖,实现了用同步的写法实现异步请求。
    C:promise是一个代表了异步操作最终完成或失败的对象。

    示例代码:

    async function getData(){
        try{
            const res = await axios.get("xx");
            console.log(res);
        }catch(err){
            console.log(err);
        }
    }
    getData();
    
  • CSS布局
    Q:描述 Flexbox 和 Grid 布局的主要区别。你更倾向于在什么情况下使用 Flexbox,什么情况下使用 Grid?
    A:flex是弹性盒子,gird是栅格布局,flex较为灵活。
    C:flexbox是一种布局方法,意味着它能够处理元素在一个方向上的空间分配,flex设计的初衷是为了提供一种有效的方式来布局、对齐和分配在容器中的项目空间,即使大小是未知或动态变化的。 一维布局;
    grid是二维布局。

  • 前端性能优化
    Q:列举至少五种你可以用来提高网页性能的方法或技术。请解释为什么这些方法会影响性能,并且你是如何决定哪些方法最适合当前项目的。
    A:1. 使用精灵图,当前端使用较多小尺寸icon的时候,可以考虑把这些合并成一个较大的精灵图,避免多次请求服务器资源;2. 首屏加载优化,可以使用服务端渲染(ssr),加载首屏时直接渲染服务端返回的html,避免因计算样式或加载js脚本影响加载速度; 3. 减少页面的重排和重绘,尽量避免使用table,因为table中一个小的变化会引起整个页面的重排; 4. 使用缓存,当资源未发生改变时,使用本地缓存可以提高用户体验; 5. 开启gzip加速,前端资源通过gzip打包放在服务器上,减少文件体积,加快获取速度。
    C:决定使用什么优化技术:分析网站或应用的性能瓶颈。

  • Web安全
    Q:解释跨站脚本攻击(XSS)和跨站请求伪造(CSRF)的区别。你会如何防御这两种攻击?
    A: XSS是通过在url、文本字段中注入script脚本实现的,比如在访问页面网址时携带参数,参数内包含script脚本,页面通过参数解析,执行恶意脚本;或者在评论区内提交一段script脚本,看到的用户都会遭受攻击;
    CSRF是利用用户的授权信息去伪造请求,比如引导用户点击危险网站,恶意脚本会利用用户的身份认证去伪造危险行为。
    为了防范XSS,可以对js代码进行转义、过滤用户输入等;防范CSRF可以防止页面注入iframe、使用csrf token验证等。


1. 实现一个 Promise.all

题目:请手写实现一个 Promise.all 函数,该函数接收一个 Promise 对象的数组作为参数,并返回一个新的 Promise 实例。新的 Promise 实例在所有输入 Promise 都成功解决时解决,返回值是一个包含所有输入 Promise 解决值的数组。如果任何一个输入 Promise 被拒绝,新的 Promise 立即拒绝,拒绝的原因是第一个拒绝的 Promise 的原因。

function promiseAll(promises) {
  let result = new Array(promises.length);
  let success = 0;
  return new Promise((resolve, reject) => {
    promises.forEach((promise, index) => {
      promise
        .then((res) => {
          result[index] = res;
          success++;
          if (success === promises.length) {
            resolve(result);
          }
        })
        .catch((e) => {
          reject(e);
        });
    });
  });
}
function promiseAllSettled(promises) {
  let result = new Array(promises.length);
  let count = 0;
  return new Promise((resolve, reject) => {
    promises.forEach((promise, index) => {
      promise
        .then((res) => {
          result[index] = { state: "fulfilled", value: res };
          count++;
          if (count === promises.length) {
            resolve(result);
          }
        })
        .catch((err) => {
          result[index] = { state: "rejected", err };
          count++;
          if (count === promises.length) {
            resolve(result);
          }
        });
    });
  });
}

let p1 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("p1");
  }, 1000);
});
let p2 = new Promise((resolve, reject) => {
  setTimeout(() => {
    resolve("p2");
  }, 2000);
});
let p3 = new Promise((resolve, reject) => {
  setTimeout(() => {
    reject("p3");
  }, 3000);
});
promiseAll([p1, p2, p3])
  .then((res) => {
    console.log(res);
  })
  .catch((err) => {
    console.log(err);
  });
promiseAllSettled([p1, p2, p3])
  .then((res) => {
    console.log(res);
  })
  .catch((err) => {
    console.log(err);
  });

2. 实现一个简单的路由

题目:在不使用任何前端路由库的情况下,如何实现一个简单的前端路由(SPA)功能?请描述你的思路,并给出基本的实现代码。

3. 实现防抖(Debounce)函数

题目:请实现一个防抖(Debounce)函数。防抖函数接收一个函数和等待时间作为参数,并返回一个新的函数。返回的新函数在被连续调用时,只有在等待时间过去后才会执行原函数,如果在等待时间内再次被调用,则重新计时。

function debounce(func, wait) {
  let timeout = null;
  return function () {
    clearTimeout(timeout);
    let arg = arguments;
    let _this = this;
    timeout = setTimeout(() => {
      func.apply(_this, arg);
    }, wait);
  };
}

let add = (a, b) => {
  console.log(a + b);
};
const debounceAdd = debounce(add, 1000);
setTimeout(() => {
  debounceAdd(1, 2);
}, 200);
setTimeout(() => {
  debounceAdd(1, 2);
}, 200);

4. 实现一个简单的模板引擎

题目:实现一个简单的模板引擎,它能够将类似于 "Hello, {{name}}!" 的模板字符串和一个对象如 {name: "World"} 作为输入,并输出 "Hello, World!"。请描述你的实现思路,并给出代码。

5. CSS 垂直居中的方法

题目:请列举至少三种使得一个元素在其父元素中垂直居中的方法,并简要描述每种方法的原理和使用场景。

  • 深拷贝
const deepClone = (obj) => {
  if (obj === null || typeof obj !== "object") return obj;
  let result = Array.isArray(obj) ? [] : {};
  for (let key in obj) {
    if (obj.hasOwnProperty(key)) {
      if (obj[key] && typeof obj[key] === "object") {
        result[key] = deepClone(obj[key]);
      } else {
        result[key] = obj[key];
      }
    }
  }
};
  • 手写instanceOf
const myInstance = (left, right) => {
  if (left !== "object" || left === null) return false;
  let proto = Object.getPrototypeOf(left);
  while (proto) {
    if (proto === right.prototype) return true;
    proto = Object.getPrototypeOf(proto);
  }
  return false;
};
  • 手写new
function myNew(fn, ...args) {
  const obj = {};
  obj.__proto__ = fn.prototype;
  let result = fn.apply(fn, args);
  return result instanceof Object ? result : obj;
}
  • 实现简单ajax
function ajax(options) {
  const xhr = new XMLHttpRequest();
  options = options || {};
  options.type = (options.type || "GET").toUpperCase();
  options.dataType = options.dataType || "json";
  const params = options.data;

  if (options.type === "GET") {
    xhr.open("GET", options.url + "?" + params, true);
    xhr.send(null);
  } else if (options.type === "POST") {
    xhr.open("POST", options.url, true);
    xhr.send(params);
  }

  xhr.onreadystatechange = function () {
    if (xhr.readyState === 4) {
      const status = xhr.status;
      if (status >= 200 && status < 300) {
        options.success && options.success(xhr.responseText, xhr.responseXML);
      } else {
        options.fail && options.fail(status);
      }
    }
  };
}

ajax({
  url: "http://localhost:3000/api",
  type: "GET",
  data: {
    name: "zhangsan",
    age: 18,
  },
  dataType: "json",
  success: function (response, xml) {
    console.log(response);
  },
  fail: function (status) {
    console.log(status);
  },
});
  • 函数柯里化
const curry = function (fn) {
  return function curried(...args) {
    if (args.length > fn.length) {
      return function () {
        return curried(...args.concat([...arguments]));
      };
    }
    return fn(...args);
  };
};
  • 节流和防抖
function debounce(func, wait) {
  let timeout = null;
  return function () {
    let args = arguments;
    let _this = this;
    if (timeout) clearTimeout(timeout);
    timeout = setTimeout(() => {
      func.apply(_this, args);
    }, wait);
  };
}

function throttle(func, wait) {
  let timeout = null;
  return function () {
    let args = arguments;
    let _this = this;
    if (!timeout) {
      timeout = setTimeout(() => {
        func.apply(_this, args);
        timeout = null;
      }, wait);
    }
  };
}

function throttle(func, wait) {
  let oldtime = Date.now();
  return function () {
    let args = arguments;
    let _this = this;
    let newtime = Date.now();
    if (newtime - oldtime >= wait) {
      func.apply(_this, args);
      oldtime = Date.now();
    }
  };
}
  • 并查集
function createUnionFind(size) {
  const parent = new Array(size).fill(0).map((_, i) => i);

  function find(x) {
    if (parent[x] === x) {
      return x;
    }
    parent[x] = find(parent[x]);
    return parent[x];
  }

  function union(x, y) {
    let rootX = find(x);
    let rootY = find(y);
    if (rootX !== rootY) {
      parent[rootX] = rootY;
    }
  }

  function connected(x, y) {
    return find(x) === find(y);
  }

  return {
    find,
    union,
    connected,
  };
}